﻿using System;
using System.Collections;
using System.Collections.Generic;
using Godot.core.math;

namespace Godot.core
{
    public partial class Variant
    {
        public enum Type
        {    
		    NIL,		
		
		    // atomic types 		
		    BOOL,
		    INT,
		    REAL,
		    STRING,
		
		    // math types
		
		    VECTOR2,		// 5
		    RECT2,
		    VECTOR3,
		    MATRIX32,
		    PLANE,
		    QUAT,			// 10
		    AABB, //sorry naming convention fail :( not like it's used often
		    MATRIX3,
		    TRANSFORM,
		
		    // misc types		
		    COLOR,
		    IMAGE,			// 15
		    NODE_PATH,
		    RID,
		    OBJECT,		
		    INPUT_EVENT,
		    DICTIONARY,		// 20
		    ARRAY,
			
		    // arrays
		    RAW_ARRAY,
		    INT_ARRAY,
		    REAL_ARRAY,
		    STRING_ARRAY,	// 25
		    VECTOR2_ARRAY,
		    VECTOR3_ARRAY,
		    COLOR_ARRAY,
			
		    VARIANT_MAX
        }

        protected static string[] type_names =
        #region ____type_names____
        {
            "Nil",		
		
		    // atomic types 		
		    "bool",
		    "int",
		    "real",
		    "String",
		
		    // math types
		
		    "Vector2",		// 5
		    "Rect2",
		    "Vector3",
		    "Matrix32",
		    "Plane",
		    "Quat",			// 10
		    "AABB", //sorry naming convention fail :( not like it's used often
		    "Matrix3",
		    "Transform",
		
		    // misc types		
		    "Color",
		    "Image",			// 15
		    "NodePath",
		    "RID",
		    "Object",		
		    "InputEvent",
		    "Dictionary",		// 20
		    "Array",
			
		    // arrays
		    "RawArray",
		    "IntArray",
		    "RealArray",
		    "StringArray",	// 25
		    "Vector2Array",
		    "Vector3Array",
		    "ColorArray"
        };
        #endregion

        public bool is_num
        {
            get
            {
                return (type == Variant.Type.INT || type == Variant.Type.REAL);
            }
        }

        public bool is_array
        {
            get
            {
                return ((int)type >= (int)Variant.Type.ARRAY);
            }
        }

        public bool is_zero
        {
            get
            {
                switch( _type ) 
                {
		            case Variant.Type.NIL: {
			            return true;
		            } break;

		            // atomic types
		            case Variant.Type.BOOL: {
			            return ((bool)_data) == false;
		            } break;

		            case Variant.Type.INT: {
			            return ((int)_data) == 0;
		            } break;

		            case Variant.Type.REAL: {
			            return ((double)_data) == 0;
		            } break;

		            case Variant.Type.STRING: {
			            return (_data != null) ? ((_data as string).Length != 0) : true;
		            } break;

		            // math types
                    case Variant.Type.VECTOR2: {
                        return ((Vector2)_data) == Vector2.empty ;
                    } break;

                    case Variant.Type.RECT2: {
                        return ((Rect2)_data) == Rect2.empty;
                    } break;

                    case Variant.Type.MATRIX32: {
                            return ((Matrix32)_data) == Matrix32.empty;
                    } break;

                    case Variant.Type.VECTOR3: {
                            return ((Vector3)_data) == Vector3.empty;
                    } break;

                    case Variant.Type.PLANE: {
                        return ((Plane)_data) == Plane.empty;
                    } break;
            
                    case Variant.Type.AABB: {
                        return ((Aabb)_data) == Aabb.empty;
                    } break;

                    case Variant.Type.QUAT: {
                        return ((Quat)_data) == Quat.empty;
                    } break;

                    case Variant.Type.MATRIX3: {
                        return ((Matrix3)_data) == Matrix3.empty;
                    } break;

                    case Variant.Type.TRANSFORM: {
                        return ((Transform)_data) == Transform.empty;
                    } break;

                    // misc types
                    case Variant.Type.COLOR: {
                        return ((Color)_data) == Color.black;
                    } break;

            //        case IMAGE: {

            //            return _data._image->empty();

            //        } break;
            //        case _RID: {

            //            return *reinterpret_cast<const RID*>(_data._mem)==RID();
            //        } break;
            //        case OBJECT: {

            //            return _get_obj().obj==NULL;
            //        } break;
            //        case NODE_PATH: {

            //            return reinterpret_cast<const NodePath*>(_data._mem)->is_empty();

            //        } break;
            //        case INPUT_EVENT: {

            //            return _data._input_event->type==InputEvent::NONE;

            //        } break;
                    
                    case Variant.Type.DICTIONARY: {
                           return ((_data as Dictionary<Variant, Variant>).Count == 0);
                    } break;

                    //arrays
                    case Variant.Type.ARRAY:
                    case Variant.Type.RAW_ARRAY:
                    case Variant.Type.INT_ARRAY:
                    case Variant.Type.REAL_ARRAY:
                    case Variant.Type.STRING_ARRAY:
                    case Variant.Type.VECTOR2_ARRAY:
                    case Variant.Type.VECTOR3_ARRAY:
                    case Variant.Type.COLOR_ARRAY: {
                        return ((System.Collections.IList)_data).Count == 0;
                    } break;

                    default: break;
	            }

	            return false;
            }
        }

        public static bool can_convert(Variant.Type p_type_from, Variant.Type p_type_to)
        {
            if (p_type_from.Equals(p_type_to))
                return true;

            if (p_type_to.Equals(Variant.Type.NIL) && !p_type_from.Equals(Variant.Type.NIL)) //nil can convert to anything
                return true;

            if (p_type_from.Equals(Variant.Type.NIL))
            {
                return p_type_to.Equals(Variant.Type.OBJECT);
            };

            List<Variant.Type> valid_types = new List<Variant.Type>();
            List<Variant.Type> invalid_types = new List<Variant.Type>();

            switch (p_type_to)
            {
                case Variant.Type.BOOL:
                    valid_types.Add(Variant.Type.INT);
                    valid_types.Add(Variant.Type.REAL);
                    valid_types.Add(Variant.Type.STRING);
                    break;

                case Variant.Type.INT:
                    valid_types.Add(Variant.Type.BOOL);
                    valid_types.Add(Variant.Type.REAL);
                    valid_types.Add(Variant.Type.STRING);
                    break;

                case Variant.Type.REAL:
                    valid_types.Add(Variant.Type.BOOL);
                    valid_types.Add(Variant.Type.INT);
                    valid_types.Add(Variant.Type.STRING);
                    break;

                case Variant.Type.STRING:
                    invalid_types.Add(Variant.Type.OBJECT);
                    invalid_types.Add(Variant.Type.IMAGE);
                    break;

                case Variant.Type.MATRIX32:
                    invalid_types.Add(Variant.Type.TRANSFORM);
                    break;

                case Variant.Type.QUAT:
                    valid_types.Add(Variant.Type.MATRIX3);
                    break;

                case Variant.Type.MATRIX3:
                    valid_types.Add(Variant.Type.QUAT);
                    break;

                case Variant.Type.TRANSFORM:
                    valid_types.Add(Variant.Type.MATRIX32);
                    valid_types.Add(Variant.Type.QUAT);
                    valid_types.Add(Variant.Type.MATRIX3);
                    break;

                case Variant.Type.RID:
                    valid_types.Add(Variant.Type.OBJECT);
                    break;

                case Variant.Type.OBJECT:
                    break;

                case Variant.Type.NODE_PATH:
                    valid_types.Add(Variant.Type.STRING);
                    break;

                case Variant.Type.ARRAY:
                    valid_types.Add(Variant.Type.RAW_ARRAY);
                    valid_types.Add(Variant.Type.INT_ARRAY);
                    valid_types.Add(Variant.Type.STRING_ARRAY);
                    valid_types.Add(Variant.Type.REAL_ARRAY);
                    valid_types.Add(Variant.Type.COLOR_ARRAY);
                    valid_types.Add(Variant.Type.VECTOR2_ARRAY);
                    valid_types.Add(Variant.Type.VECTOR3_ARRAY);
                    break;

                // arrays
                case Variant.Type.RAW_ARRAY:
                    valid_types.Add(Variant.Type.ARRAY);
                    break;

                case Variant.Type.INT_ARRAY:
                    valid_types.Add(Variant.Type.ARRAY);
                    break;

                case Variant.Type.REAL_ARRAY:
                    valid_types.Add(Variant.Type.ARRAY);
                    break;

                case Variant.Type.STRING_ARRAY:
                    valid_types.Add(Variant.Type.ARRAY);
                    break;

                case Variant.Type.VECTOR2_ARRAY:
                    valid_types.Add(Variant.Type.ARRAY);
                    break;

                case Variant.Type.VECTOR3_ARRAY:
                    valid_types.Add(Variant.Type.ARRAY);
                    break;

                case Variant.Type.COLOR_ARRAY:
                    valid_types.Add(Variant.Type.ARRAY);
                    break;

                default: break;
            }


            if (valid_types.Count > 0)
            {
                return valid_types.Contains(p_type_from);
            }
            else if (invalid_types.Count > 0)
            {
                return !invalid_types.Contains(p_type_from);
            }

            return false;
        }

        public static string get_type_name(Variant.Type p_type)
        {
            if(!Enum.IsDefined(typeof(Variant.Type), p_type) || p_type.Equals(Variant.Type.VARIANT_MAX))
                return "";
            return type_names[(int)p_type];
        }
    }
}
